using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Xml;
using System.Xml.Linq;
using System.Linq;


namespace GuavaData
{
    public class TransformException : Exception
    {
        public TransformException(string s)
            : base(s)
        {
            return;
        }
        public TransformException(string s, Exception e)
            : base(s, e)
        {
            return;
        }
    }
    
    public abstract class Transform : Object, Cell
    {
        Cell child;

        public bool hasChild()
        {
            return true;
        }

        public Cell getChild()
        {
            return child;
        }

        internal virtual DataSet XResult(DataSet ds)
        {
            return ds;
        }

        public virtual Transform Inverse()
        {
            return this;
        }

        public virtual List<XElement> ToXElement(){
            return null;
        }
        public static Transform FromXElement(XElement xe)
        {
            if (xe.Name.LocalName != "Transform")
                throw new Exception("XElement is not of type Transform");

            // What type should the new transfrom be?
            var type = xe.Attribute("type").Value;

            if (type == "VMerge")
                return VMerge.FromXElement(xe);
            if (type == "VSplit")
                return VSplit.FromXElement(xe);
            if (type == "HMerge")
                return HMerge.FromXElement(xe);
            if (type == "HSplit")
                return HSplit.FromXElement(xe);
            if (type == "Unpivot")
                return Unpivot.FromXElement(xe);
            if (type == "Apply")
                return Apply.FromXElement(xe);
            if (type == "Audit")
                return Audit.FromXElement(xe);
            if (type == "Adorn")
                return Adorn.FromXElement(xe);
            if (type == "Pivot")
                return Pivot.FromXElement(xe);
            throw new Exception("Transformation type is unknown");
        }

        public virtual DataSet Process(Transaction o)
        {
            foreach (Command c in o.Contents)
                c.root.Visit(this, c);
            o.Complete();
            return child.Process(o);
        }

        internal Cell Child
        {
            get
            {
                return child;
            }
            set
            {
                this.child = value;
            }
        }

        #region Operator-Specific Transforms
        internal virtual void Expand(AddColumn ac, Command c)
        {
            return;
        }
        internal virtual void Expand(AddElement ae, Command c)
        {
            return;
        }
        internal virtual void Expand(AddTable at, Command c)
        {
            return;
        }
        internal virtual void Expand(AlterColumnRename acr, Command c)
        {
            return;
        }
        internal virtual void Expand(AlterElementRename aer, Command c)
        {
            return;
        }
        internal virtual void Expand(AlterTableRename atr, Command c)
        {
            return;
        }
        internal virtual void Expand(AntiSemiJoin asj, Command c)
        {
            asj.Child1.Visit(this, c);
            asj.Child2.Visit(this, c);
        }
        internal virtual void Expand(ApplyOp ao, Command c)
        {
            ao.Child.Visit(this, c);
        }
        internal virtual void Expand(Check ch, Command c)
        {
            ch.Query1.Visit(this, c);
            ch.Query2.Visit(this, c);
        }
        internal virtual void Expand(CrossJoin cj, Command c)
        {
            cj.Child1.Visit(this, c);
            cj.Child2.Visit(this, c);
        }
        internal virtual void Expand(Delete d, Command c)
        {
            return;
        }
        internal virtual void Expand(DropColumn dc, Command c)
        {
            return;
        }
        internal virtual void Expand(DropElement de, Command c)
        {
            return;
        }
        internal virtual void Expand(DropForeignKey dfk, Command c)
        {
            return;
        }
        internal virtual void Expand(DropTable dt, Command c)
        {
            return;
        }
        internal virtual void Expand(Error er, Command c)
        {
            er.Query.Visit(this, c);
        }
        internal virtual void Expand(Except e, Command c)
        {
            e.Child1.Visit(this, c);
            e.Child2.Visit(this, c);
        }
        internal virtual void Expand(Filter f, Command c)
        {
            f.Child.Visit(this, c);
        }
        internal virtual void Expand(ForeignKey fk, Command c)
        {
            return;
        }
        internal virtual void Expand(Insert i, Command c)
        {
            i.Child.Visit(this, c);
        }
        internal virtual void Expand(Intersect i, Command c)
        {
            foreach (Operator o in i.Children)
                o.Visit(this, c);
        }
        internal virtual void Expand(Join j, Command c)
        {
            j.Child1.Visit(this, c);
            j.Child2.Visit(this, c);
        }
        internal virtual void Expand(Operator o, Command c)
        {
            throw new Exception("Unexpected operator discovered of type " + o.GetType().Name + ".");
        }
        internal virtual void Expand(PivotOp po, Command c)
        {
            po.Child.Visit(this, c);
        }
        internal virtual void Expand(Project p, Command c)
        {
            p.Child.Visit(this, c);
        }
        internal virtual void Expand(RowConstant tc, Command c)
        {
            return;
        }
        internal virtual void Expand(TableRef tr, Command c)
        {
            return;
        }
        internal virtual void Expand(Union u, Command c)
        {
            foreach (Operator o in u.Children)
                o.Visit(this, c);
        }
        internal virtual void Expand(UnpivotOp uo, Command c)
        {
            uo.Child.Visit(this, c);
        }
        internal virtual void Expand(Update u, Command c)
        {
            u.Child.Visit(this, c);
        }
        #endregion

        public List<SemanticUnit> SemanticUnits(string table)
        {
            if (this is SemanticTransform)
            {
                List<SemanticUnit> lsu = child.SemanticUnits(table);
                (this as SemanticTransform).BuildUnits(table, lsu);
                return lsu;
            }
            else
                return child.SemanticUnits(table);
        }
    }

    public abstract class SemanticTransform : Transform
    {
        public virtual void BuildUnits(string table, List<SemanticUnit> ls)
        {
            return;
        }
    }

    public abstract class SemanticUnit : Object
    {
        public abstract string UnitType { get;}
    }

    public class AddColumnUnit : SemanticUnit
    {
        private GEmpty column;
        
        public AddColumnUnit(string name, Domain.Domain dom)
        {
            column = new GEmpty(name, dom);
        }
        
        public override string UnitType
        {
            get
            {
                return "AddColumn";
            }
        }

        public GEmpty Column
        {
            get
            {
                return column;
            }
        }
    }

    public class RedundantColumnUnit : SemanticUnit
    {
        private Column column;
        private GSeed location;

        public Column Column
        {
            get { return column; }
        }

        public GSeed Location
        {
            get { return location; }
        }

        public RedundantColumnUnit(string name, Domain.Domain dom, GSeed location)
        {
            this.location = location;
            this.column = new Column(name, dom);
        }

        public override string UnitType
        {
            get
            {
                return "RedundantColumn";
            }
        }
    }

    public class AssertConditionUnit : SemanticUnit
    {

        public override string UnitType
        {
            get { return "AssertCondition"; }
        }
    }

    public class DeprecatedColumnUnit : SemanticUnit
    {
        private Guava column;

        public DeprecatedColumnUnit(string name, Domain.Domain dom)
        {
            column = new GEmpty(name, dom);
        }

        public DeprecatedColumnUnit(Guava column)
        {
            this.column = column;
        }

        public override string UnitType
        {
            get
            {
                return "DeprecatedColumn";
            }
        }

        public Guava Column
        {
            get
            {
                return column;
            }
        }
    }

    public class DeprecatedElementUnit : SemanticUnit
    {
        private string column;
        private string element;

        public DeprecatedElementUnit(string name, string elem)
        {
            column = name;
            element = elem;
        }

        public override string UnitType
        {
            get
            {
                return "DeprecatedElement";
            }
        }

        public string Column
        {
            get
            {
                return column;
            }
        }

        public string Element
        {
            get
            {
                return element;
            }
        }
    }
}
